// Copyright (c) 2009, Tom Lokovic
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

using System;

namespace Byond.Midi
{
	/// <summary>
	/// Extension methods for the Instrument enum.
	/// </summary>
	public static class InstrumentExtensionMethods
	{
		/// <summary>
		/// Returns true if the specified instrument is valid.
		/// </summary>
		/// <param name="instrument">The instrument to test.</param>
		public static bool IsValid(this Instrument instrument)
		{
			return (int)instrument >= 0 && (int)instrument < 128;
		}

		/// <summary>
		/// Throws an exception if instrument is not valid.
		/// </summary>
		/// <param name="instrument">The instrument to validate.</param>
		/// <exception cref="ArgumentOutOfRangeException">The instrument is out-of-range.
		/// </exception>
		public static void Validate(this Instrument instrument)
		{
			if (!instrument.IsValid())
				throw new ArgumentOutOfRangeException("instrument");
		}

		/// <summary>
		/// General Midi instrument names, used by GetInstrumentName().
		/// </summary>
		private static readonly string[] InstrumentNames = new[]
			{
				// Piano Family:
				"Acoustic Grand Piano",
				"Bright Acoustic Piano",
				"Electric Grand Piano",
				"Honky-tonk Piano",
				"Electric Piano 1",
				"Electric Piano 2",
				"Harpsichord",
				"Clavinet",
				
				// Chromatic Percussion Family:
				"Celesta",
				"Glockenspiel",
				"Music Box",
				"Vibraphone",
				"Marimba",
				"Xylophone",
				"Tubular Bells",
				"Dulcimer",
				
				// Organ Family:
				"Drawbar Organ",
				"Percussive Organ",
				"Rock Organ",
				"Church Organ",
				"Reed Organ",
				"Accordion",
				"Harmonica",
				"Tango Accordion",
				
				// Guitar Family:
				"Acoustic Guitar (nylon)",
				"Acoustic Guitar (steel)",
				"Electric Guitar (jazz)",
				"Electric Guitar (clean)",
				"Electric Guitar (muted)",
				"Overdriven Guitar",
				"Distortion Guitar",
				"Guitar harmonics",
				
				// Bass Family:
				"Acoustic Bass",
				"Electric Bass (finger)",
				"Electric Bass (pick)",
				"Fretless Bass",
				"Slap Bass 1",
				"Slap Bass 2",
				"Synth Bass 1",
				"Synth Bass 2",
				
				// Strings Family:
				"Violin",
				"Viola",
				"Cello",
				"Contrabass",
				"Tremolo Strings",
				"Pizzicato Strings",
				"Orchestral Harp",
				"Timpani",
				
				// Ensemble Family:
				"String Ensemble 1",
				"String Ensemble 2",
				"Synth Strings 1",
				"Synth Strings 2",
				"Choir Aahs",
				"Voice Oohs",
				"Synth Voice",
				"Orchestra Hit",
				
				// Brass Family:
				"Trumpet",
				"Trombone",
				"Tuba",
				"Muted Trumpet",
				"French Horn",
				"Brass Section",
				"Synth Brass 1",
				"Synth Brass 2",
				
				// Reed Family:
				"Soprano Sax",
				"Alto Sax",
				"Tenor Sax",
				"Baritone Sax",
				"Oboe",
				"English Horn",
				"Bassoon",
				"Clarinet",
				
				// Pipe Family:
				"Piccolo",
				"Flute",
				"Recorder",
				"Pan Flute",
				"Blown Bottle",
				"Shakuhachi",
				"Whistle",
				"Ocarina",
				
				// Synth Lead Family:
				"Lead 1 (square)",
				"Lead 2 (sawtooth)",
				"Lead 3 (calliope)",
				"Lead 4 (chiff)",
				"Lead 5 (charang)",
				"Lead 6 (voice)",
				"Lead 7 (fifths)",
				"Lead 8 (bass + lead)",
				
				// Synth Pad Family:
				"Pad 1 (new age)",
				"Pad 2 (warm)",
				"Pad 3 (polysynth)",
				"Pad 4 (choir)",
				"Pad 5 (bowed)",
				"Pad 6 (metallic)",
				"Pad 7 (halo)",
				"Pad 8 (sweep)",
				
				// Synth Effects Family:
				"FX 1 (rain)",
				"FX 2 (soundtrack)",
				"FX 3 (crystal)",
				"FX 4 (atmosphere)",
				"FX 5 (brightness)",
				"FX 6 (goblins)",
				"FX 7 (echoes)",
				"FX 8 (sci-fi)",
				
				// Ethnic Family:
				"Sitar",
				"Banjo",
				"Shamisen",
				"Koto",
				"Kalimba",
				"Bag pipe",
				"Fiddle",
				"Shanai",
				
				// Percussive Family:
				"Tinkle Bell",
				"Agogo",
				"Steel Drums",
				"Woodblock",
				"Taiko Drum",
				"Melodic Tom",
				"Synth Drum",
				"Reverse Cymbal",
				
				// Sound Effects Family:
				"Guitar Fret Noise",
				"Breath Noise",
				"Seashore",
				"Bird Tweet",
				"Telephone Ring",
				"Helicopter",
				"Applause",
				"Gunshot"
			};

		/// <summary>
		/// Returns the human-readable name of a MIDI instrument.
		/// </summary>
		/// <param name="instrument">The instrument.</param>
		/// <exception cref="ArgumentOutOfRangeException">The instrument is out-of-range.
		/// </exception>
		public static string Name(this Instrument instrument)
		{
			instrument.Validate();
			return InstrumentNames[(int)instrument];
		}
	}
}